using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace AvalonDock
{
    /// <summary>
    /// Represent a document which can be host by a <see cref="DocumentPane"/>.
    /// </summary>
    /// <remarks>A document is always hosted by a <see cref="DocumentPane"/> usually in the central area of <see cref="DockingManager"/>.
    /// It has limited dragging features becaus it can be only moved to an other <see cref="DocumentPane"/> and can't float as a separate window.
    /// You can access all documents within <see cref="DockingManager"/> with property <see cref="DockingManager.Documents"/>.</remarks>
    public class DocumentContent : ManagedContent
    {
        #region Constants and Fields

        /// <summary>
        /// ContentTypeDescription Dependency Property
        /// </summary>
        public static readonly DependencyProperty ContentTypeDescriptionProperty =
            DependencyProperty.Register(
                "ContentTypeDescription",
                typeof(string),
                typeof(DocumentContent),
                new FrameworkPropertyMetadata(string.Empty));

        /// <summary>
        /// InfoTip Dependency Property
        /// </summary>
        public static readonly DependencyProperty InfoTipProperty = DependencyProperty.Register(
            "InfoTip", typeof(string), typeof(DocumentContent), new FrameworkPropertyMetadata(null));

        // Using a DependencyProperty as the backing store for IsFloatingAllowed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsFloatingAllowedProperty =
            DependencyProperty.Register(
                "IsFloatingAllowed", typeof(bool), typeof(DocumentContent), new PropertyMetadata(true));

        /// <summary>
        /// IsFloating Read-Only Dependency Property
        /// </summary>
        private static readonly DependencyPropertyKey IsFloatingPropertyKey =
            DependencyProperty.RegisterReadOnly(
                "IsFloating", typeof(bool), typeof(DocumentContent), new FrameworkPropertyMetadata(false));

        public static readonly DependencyProperty IsFloatingProperty = IsFloatingPropertyKey.DependencyProperty;

        #endregion

        #region Constructors and Destructors

        static DocumentContent()
        {
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(DocumentContent), new FrameworkPropertyMetadata(typeof(DocumentContent)));
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the ContentTypeDescription property.  This dependency property 
        /// indicates a text which describes the type of content contained in this document.
        /// </summary>
        public string ContentTypeDescription
        {
            get
            {
                return (string)GetValue(ContentTypeDescriptionProperty);
            }
            set
            {
                SetValue(ContentTypeDescriptionProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the InfoTip property.  This dependency property 
        /// indicates information text attached to the document content.
        /// </summary>
        /// <remarks>This text is usually displayed when users switch between documents and helps them to choose the right one.</remarks>
        public string InfoTip
        {
            get
            {
                return (string)GetValue(InfoTipProperty);
            }
            set
            {
                SetValue(InfoTipProperty, value);
            }
        }

        /// <summary>
        /// Gets the IsFloating property.  This dependency property 
        /// indicates if the DocumentContent is floating inside an external window.
        /// </summary>
        public bool IsFloating
        {
            get
            {
                return (bool)GetValue(IsFloatingProperty);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating if this document can float over main window (VS2010 Feature).
        /// </summary>
        public bool IsFloatingAllowed
        {
            get
            {
                return (bool)GetValue(IsFloatingAllowedProperty);
            }
            set
            {
                SetValue(IsFloatingAllowedProperty, value);
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// Activate the document showing its header if it's not visible
        /// </summary>
        public override void Activate()
        {
            base.Activate();

            if (!DocumentTabPanel.GetIsHeaderVisible(this))
            {
                var parentPane = ContainerPane as DocumentPane;
                if (parentPane != null && parentPane.GetManager() != null && parentPane.Items.IndexOf(this) != 0)
                {
                    parentPane.Items.Remove(this);
                    parentPane.Items.Insert(0, this);
                    parentPane.SelectedIndex = 0;
                }
            }

            //Active this content as the active document
            if (Manager != null)
            {
                Manager.ActiveDocument = this;
            }

            //ensure this content is rendered first
            Panel.SetZIndex(this, 2);

            if (DefaultElement != null)
            {
                DefaultElement.Focus();
            }
        }

        /// <summary>
        /// Close the content
        /// </summary>
        /// <returns>
        /// Returns true if the content was succesfully closed, false otherwise.
        /// </returns>
        public override bool Close()
        {
            if (!IsCloseable)
            {
                return false;
            }

            var e = new CancelEventArgs(false);
            OnClosing(e);

            if (e.Cancel)
            {
                return false;
            }

            var oldManager = Manager;

            if (oldManager != null)
            {
                oldManager.FireDocumentClosingEvent(e);
            }

            if (e.Cancel)
            {
                return false;
            }

            InternalClose();

            OnClosed();

            //if documents are attached to an external source via DockingManager.DocumentsSource
            //let application host handle the document closing by itself
            if (oldManager != null && oldManager.DocumentsSource != null)
            {
                oldManager.HandleDocumentClose(this);
            }

            if (oldManager != null)
            {
                oldManager.FireDocumentClosedEvent();
            }

            Debug.Assert(Parent == null, "Parent MUST bu null after Doc is closed");
            return true;
        }

        /// <summary>
        /// Hide the <see cref="DocumentContent"/> (Close the document)
        /// </summary>
        /// <returns></returns>
        public override bool Hide()
        {
            return Close();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (DragEnabledArea != null)
            {
                if (string.IsNullOrEmpty((string)DragEnabledArea.ToolTip))
                {
                    DragEnabledArea.ToolTip = InfoTip;
                }
            }
        }

        /// <summary>
        /// Show <see cref="DocumentContent"/> as tabbed document
        /// </summary>
        public override void Show()
        {
            Show(false);
        }

        /// <summary>
        /// Show <see cref="DocumentContent"/> as tabbed document inside the provided <see cref="DockingManager"/>
        /// </summary>
        /// <param name="manager">Docking manager target</param>
        public override void Show(DockingManager manager)
        {
            Show(manager, false);
        }

        /// <summary>
        /// Show <see cref="DocumentContent"/> as tabbed document or inside a floating window
        /// </summary>
        public void Show(bool showAsFloatingWindow)
        {
            if (!CanExecuteCommand(ManagedContentCommands.Show))
            {
                throw new InvalidOperationException("This operation can be executed in this state");
            }

            Manager.Show(this, showAsFloatingWindow);
        }

        /// <summary>
        /// Show <see cref="DocumentContent"/> as tabbed document inside the provided <see cref="DockingManager"/>
        /// </summary>
        /// <param name="manager">Docking manager target</param>
        /// <param name="showAsFloatingWindow">True if document should be shown inside a floating window (<see cref="DocumentFloatingWindow"/>)</param>
        public void Show(DockingManager manager, bool showAsFloatingWindow)
        {
            if (Manager != null && !Equals(Manager, manager))
            {
                throw new InvalidOperationException(
                    "Please remove the content from previous DockingManager (using the Close method)");
            }

            if (!CanExecuteCommand(ManagedContentCommands.Show))
            {
                throw new InvalidOperationException("This operation can be executed in this state");
            }

            manager.Show(this, showAsFloatingWindow);

            manager.Documents.Add(this);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Close this content without notifications
        /// </summary>
        internal void InternalClose()
        {
            var manager = Manager;
            var parentPane = ContainerPane as DocumentPane;
            var floatingParentPane = ContainerPane as FloatingDocumentPane;

            if (floatingParentPane != null)
            {
                floatingParentPane.RemoveContent(0);
                if (floatingParentPane.FloatingWindow != null && !floatingParentPane.FloatingWindow.IsClosing)
                {
                    floatingParentPane.FloatingWindow.Close();
                }
            }
            else if (parentPane != null)
            {
                parentPane.Items.Remove(this);

                parentPane.CheckContentsEmpty();
            }

            if (manager != null)
            {
                if (Equals(manager.ActiveContent, this))
                {
                    manager.ActiveContent = null;
                }

                if (Equals(manager.ActiveDocument, this))
                {
                    manager.ActiveDocument = null;
                }
            }
        }

        /// <summary>
        /// Provides a secure method for setting the IsFloating property.  
        /// This dependency property indicates if the DocumentContent is floating inside an external window.
        /// </summary>
        /// <param name="value">The new value for the property.</param>
        internal void SetIsFloating(bool value)
        {
            SetValue(IsFloatingPropertyKey, value);
        }

        /// <summary>
        /// Retrive a value indicating if the command can be executed based to the dockable content state
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        protected override bool CanExecuteCommand(ICommand command)
        {
            if (command == DocumentContentCommands.FloatingDocument)
            {
                return !IsFloating && IsFloatingAllowed;
            }

            if (command == DocumentContentCommands.TabbedDocument)
            {
                return IsFloating;
            }

            return true;
        }

        protected override void OnDragStart(Point ptMouse, Point ptRelativeMouse)
        {
            if (IsFloatingAllowed)
            {
                Manager.Drag(this, HelperFunc.PointToScreenWithoutFlowDirection(this, ptMouse), ptRelativeMouse);
            }

            base.OnDragStart(ptMouse, ptRelativeMouse);
        }

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            CommandBindings.Add(
                new CommandBinding(DocumentContentCommands.FloatingDocument, OnExecuteCommand, OnCanExecuteCommand));
            CommandBindings.Add(
                new CommandBinding(DocumentContentCommands.TabbedDocument, OnExecuteCommand, OnCanExecuteCommand));
        }

        private void OnCanExecuteCommand(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanExecuteCommand(e.Command);
        }

        private void OnExecuteCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (!e.Handled && e.Command == DocumentContentCommands.FloatingDocument)
            {
                Show(true);
                Activate();
                e.Handled = true;
            }
            else if (!e.Handled && e.Command == DocumentContentCommands.TabbedDocument)
            {
                Show(false);
                Activate();
                e.Handled = true;
            }
        }

        #endregion
    }
}